﻿using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
using WinQuickIP.Properties;

namespace WinQuickIP
{
    /// <summary>
    /// Helper methods for the main form.  Almost a repository :)
    /// </summary>
    public static class IPUtils
    {
        /// <summary>
        /// Get a list of all enabled adapters to be displayed in the main form
        /// </summary>
        /// <returns>List of enabled adapters</returns>
        public static IList<Adapter> GetAdapters()
        {
            var adapters = new List<Adapter>();
            ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
            ManagementObjectCollection objMOC = objMC.GetInstances();

            foreach (ManagementObject mo in objMOC)
            {
                var win32Adapter = new Win32_NetworkAdapterConfiguration(mo);
                if (!win32Adapter.IPEnabled)
                    continue;

                var adapter = new Adapter { Description = win32Adapter.Description, Index = win32Adapter.Index };
                adapters.Add(adapter);
            }
            return adapters;
        }

        /// <summary>
        /// Get a Win32_NetworkAdapterConfiguration based on the index
        /// </summary>
        /// <param name="index">Index of adapter to fetch</param>
        /// <returns>Win32_NetworkAdapterConfiguration if present, null otherwise</returns>
        public static Win32_NetworkAdapterConfiguration GetWin32Adapter(uint index)
        {
            ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
            ManagementObjectCollection objMOC = objMC.GetInstances();

            foreach (ManagementObject mo in objMOC)
            {
                var adapter = new Win32_NetworkAdapterConfiguration(mo);
                if (adapter.Index == index)
                    return adapter;
            }
            return null;
        }

        /// <summary>
        /// Get IPRepresentation of the Win32_NetworkAdapterConfiguration adapter
        /// </summary>
        /// <param name="adapterIndex">Index of adapter to retrieve values from</param>
        /// <returns>IPRepresentation of the adapter</returns>
        public static IPRepresentation GetIPSettings(uint adapterIndex)
        {
            var adapter = GetWin32Adapter(adapterIndex);
            var ip = new IPRepresentation
                         {
                             Adapter = adapter.Index,
                             DHCP = adapter.DHCPEnabled
                         };

            var gateways = adapter.DefaultIPGateway;
            if (gateways != null && gateways.Length > 0)
                ip.Gateway = gateways[0];

            var ips = adapter.IPAddress;
            if (ips != null && ips.Length > 0)
                ip.IP = ips[0];


            var netmasks = adapter.IPSubnet;
            if (netmasks != null && netmasks.Length > 0)
                ip.Netmask = netmasks[0];

            var dnses = adapter.DNSServerSearchOrder;
            if (dnses != null && dnses.Length > 0)
                ip.DNS1 = dnses[0];
            if (dnses != null && dnses.Length > 1)
                ip.DNS2 = dnses[1];

            return ip;
        }

        /// <summary>
        /// Changes the adapter based on the IPRepresentation passed in
        /// </summary>
        /// <param name="ip"></param>
        /// <returns>true if reboot is required, false otherwise</returns>
        public static bool ChangeIP(IPRepresentation ip)
        {
            var rebootRequired = false;
            uint response = 0;

            var adapter = GetWin32Adapter(ip.Adapter);

            if (adapter.DHCPEnabled && ip.DHCP)
                return false;

            var ipAddresses = ip.DHCP ? new string[] { } : new[] { ip.IP };
            var subnetMasks = ip.DHCP ? new string[] { } : new[] { ip.Netmask };
            if (!ip.DHCP)
            {
                response = adapter.EnableStatic(ipAddresses, subnetMasks);
                rebootRequired = GetRebootRequired(response, "set static IP settings");
            }

            var gatewayIps = ip.DHCP ? new string[] { } : new[] { ip.Gateway };
            var gatewayCosts = ip.DHCP ? new uint[] { } : new uint[] { 1 };
            if (string.IsNullOrEmpty(ip.Gateway) && !ip.DHCP)
            {
                gatewayCosts = new uint[] { };
                gatewayIps = new string[] { };
            }
            response = adapter.SetGateways(gatewayIps, gatewayCosts);
            rebootRequired = rebootRequired || GetRebootRequired(response, "set gateways");

            List<string> dnses = new List<string>();
            if (!ip.DHCP)
            {
                if (!string.IsNullOrEmpty(ip.DNS1))
                    dnses.Add(ip.DNS1);
                if (!string.IsNullOrEmpty(ip.DNS2))
                    dnses.Add(ip.DNS2);
            }

            response = adapter.SetDNSServerSearchOrder(dnses.ToArray());
            rebootRequired = rebootRequired || GetRebootRequired(response, "set DNS servers");
            if (ip.DHCP)
            {
                response = adapter.EnableDHCP();
                rebootRequired = rebootRequired || GetRebootRequired(response, "enable DHCP");
                response = adapter.ReleaseDHCPLease();
                if (response != 83)
                    rebootRequired = rebootRequired || GetRebootRequired(response, "release DHCP lease");
                response = adapter.RenewDHCPLease();
                rebootRequired = rebootRequired || GetRebootRequired(response, "renew DHCP lease");
            }
            return rebootRequired;
        }

        private static bool GetRebootRequired(uint response, string operation)
        {
            if (response == 1)
                return true;
            if (response > 1)
                throw new Exception("Failed to " + operation + ": " + response + ". "
                    + Win32_NetworkAdapterConfiguration.GetResponseMessage(response));
            return false;
        }

        /// <summary>
        /// Get a list of IPProfiles that are stored in the Settings for the application
        /// </summary>
        /// <returns>List of IPProfile</returns>
        public static List<IPProfile> LoadProfiles()
        {
            var dict = new List<IPProfile>();
            var profStr = Settings.Default.Profiles;
            if (profStr.Length == 0)
                return dict;
            var profiles = Settings.Default.Profiles.Split('|');

            foreach (var profile in profiles)
            {
                var parts = profile.Split(',');
                var profileName = parts[0];
                var dhcp = parts[1] == "1";
                var ip = parts[2];
                var netmask = parts[3];
                var gateway = parts[4];
                var dns1 = parts[5];
                var dns2 = parts[6];
                var ipRep = new IPRepresentation
                {
                    DHCP = dhcp,
                    DNS1 = dns1,
                    DNS2 = dns2,
                    Gateway = gateway,
                    IP = ip,
                    Netmask = netmask
                };
                var prof = new IPProfile { Name = profileName, IP = ipRep };
                dict.Add(prof);
            }
            return dict;
        }

        /// <summary>
        /// Saves a list of IPProfile to the Settings of the application
        /// </summary>
        /// <param name="profiles">List of IPProfile to save</param>
        public static void SaveProfiles(List<IPProfile> profiles)
        {
            if (profiles.Count == 0)
            {
                Settings.Default.Profiles = "";
                Settings.Default.Save();
                return;
            }
            StringBuilder sb = new StringBuilder();
            foreach (var profile in profiles)
            {
                var ip = profile.IP;
                sb.Append("|");
                sb.Append(profile.Name);
                sb.Append(",");
                sb.Append(ip.DHCP ? "1" : "0");
                sb.Append(",");
                sb.Append(ip.IP);
                sb.Append(",");
                sb.Append(ip.Netmask);
                sb.Append(",");
                sb.Append(ip.Gateway);
                sb.Append(",");
                sb.Append(ip.DNS1);
                sb.Append(",");
                sb.Append(ip.DNS2);
            }
            Settings.Default.Profiles = sb.ToString().Substring(1);
            Settings.Default.Save();
        }
    }
}